﻿using System.Collections.Generic;
using System.Security.AccessControl;
using System.Text;
using System.Linq;

namespace System.IO
{
    public abstract class FileAccessorBase : IFileAccessor
    {
        private const int BUFFER_SIZE = 4096;

        public StreamReader OpenText(string path)
        {
            return new StreamReader(OpenRead(path));
        }

        public StreamWriter CreateText(string path)
        {
            return new StreamWriter(OpenWrite(path));
        }

        public StreamWriter AppendText(string path)
        {
            return new StreamWriter(Open(path, FileMode.Append));
        }

        public void Copy(string sourceFileName, string destFileName)
        {
            Copy(sourceFileName, destFileName, false);
        }

        public abstract void Copy(string sourceFileName, string destFileName, bool overwrite);

        public Stream Create(string path)
        {
            return Create(path, BUFFER_SIZE);
        }

        public Stream Create(string path, int bufferSize)
        {
            return Create(path, bufferSize, FileOptions.None);
        }

        public Stream Create(string path, int bufferSize, FileOptions options)
        {
            return Create(path, bufferSize, options, null);
        }

        public abstract Stream Create(string path, int bufferSize, FileOptions options, FileSecurity fileSecurity);

        public abstract void Delete(string path);

        public abstract void Decrypt(string path);

        public abstract void Encrypt(string path);

        public abstract bool Exists(string path);

        public Stream Open(string path, FileMode mode)
        {
            return Open(path, mode, FileAccess.ReadWrite);
        }

        public Stream Open(string path, FileMode mode, FileAccess access)
        {
            return Open(path, mode, access, FileShare.None);
        }

        public abstract Stream Open(string path, FileMode mode, FileAccess access, FileShare share);

        public void SetCreationTime(string path, DateTime creationTime)
        {
            SetCreationTimeUtc(path, creationTime.ToUniversalTime());
        }

        public abstract void SetCreationTimeUtc(string path, DateTime creationTimeUtc);

        public DateTime GetCreationTime(string path)
        {
            return GetCreationTimeUtc(path).ToLocalTime();
        }

        public abstract DateTime GetCreationTimeUtc(string path);

        public void SetLastAccessTime(string path, DateTime lastAccessTime)
        {
            SetLastAccessTimeUtc(path, lastAccessTime.ToUniversalTime());
        }

        public abstract void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc);

        public DateTime GetLastAccessTime(string path)
        {
            return GetLastAccessTimeUtc(path).ToLocalTime();
        }

        public abstract DateTime GetLastAccessTimeUtc(string path);

        public void SetLastWriteTime(string path, DateTime lastWriteTime)
        {
            SetLastWriteTimeUtc(path, lastWriteTime.ToUniversalTime());
        }

        public abstract void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc);

        public DateTime GetLastWriteTime(string path)
        {
            return GetLastWriteTimeUtc(path).ToLocalTime();
        }

        public abstract DateTime GetLastWriteTimeUtc(string path);

        public abstract FileAttributes GetAttributes(string path);

        public abstract void SetAttributes(string path, FileAttributes fileAttributes);

        public abstract FileSecurity GetAccessControl(string path);

        public abstract FileSecurity GetAccessControl(string path, AccessControlSections includeSections);

        public abstract void SetAccessControl(string path, FileSecurity fileSecurity);

        public Stream OpenRead(string path)
        {
            return Open(path, FileMode.Open);
        }

        public Stream OpenWrite(string path)
        {
            return Open(path, FileMode.Truncate);
        }

        public string ReadAllText(string path)
        {
            return ReadAllText(path, Encoding.UTF8);
        }

        public string ReadAllText(string path, Encoding encoding)
        {
            using (var stream = OpenRead(path))
            using (var reader = new StreamReader(stream, encoding))
            {
                return reader.ReadToEnd();
            }
        }

        public void WriteAllText(string path, string contents)
        {
            WriteAllText(path, contents, Encoding.UTF8);
        }

        public void WriteAllText(string path, string contents, Encoding encoding)
        {
            using (var stream = OpenWrite(path))
            using (var writer = new StreamWriter(stream, encoding))
            {
                writer.Write(contents);
            }
        }

        public byte[] ReadAllBytes(string path)
        {
            using (var stream = OpenRead(path))
            {
                var result = new byte[stream.Length];
                stream.Read(result, 0, result.Length);
                return result;
            }
        }

        public void WriteAllBytes(string path, byte[] bytes)
        {
            using (var stream = OpenWrite(path))
            {
                stream.Write(bytes, 0, bytes.Length);
            }
        }

        private static Encoding DefaultEncoding
        {
            get { return Encoding.UTF8; }
        }

        public string[] ReadAllLines(string path)
        {
            return ReadAllLines(path, DefaultEncoding);
        }

        public string[] ReadAllLines(string path, Encoding encoding)
        {
            return ReadAllText(path, encoding).Split(new[] { Environment.NewLine }, StringSplitOptions.None);
        }

        public IEnumerable<string> ReadLines(string path)
        {
            return ReadAllText(path, DefaultEncoding).Split(new[] { Environment.NewLine }, StringSplitOptions.None);
        }

        public IEnumerable<string> ReadLines(string path, Encoding encoding)
        {
            return ReadAllText(path, encoding).Split(new[] { Environment.NewLine }, StringSplitOptions.None);            
        }

        public void WriteAllLines(string path, string[] contents)
        {
            WriteAllLines(path, contents, DefaultEncoding);
        }

        public void WriteAllLines(string path, string[] contents, Encoding encoding)
        {
            WriteAllText(path, string.Join(Environment.NewLine, contents));
        }

        public void WriteAllLines(string path, IEnumerable<string> contents)
        {
            WriteAllLines(path, contents.ToArray());
        }

        public void WriteAllLines(string path, IEnumerable<string> contents, Encoding encoding)
        {
            WriteAllLines(path, contents.ToArray(), encoding);
        }

        public void AppendAllText(string path, string contents)
        {
            AppendAllText(path, contents, DefaultEncoding);
        }

        public void AppendAllText(string path, string contents, Encoding encoding)
        {
            using (var stream = Open(path, FileMode.Append))
            using (var writer = new StreamWriter(stream, encoding))
            {
                writer.Write(contents);
            }
        }

        public void AppendAllLines(string path, IEnumerable<string> contents)
        {
            AppendAllLines(path, contents, DefaultEncoding);
        }

        public void AppendAllLines(string path, IEnumerable<string> contents, Encoding encoding)
        {
            AppendAllText(path, string.Join(Environment.NewLine, contents), encoding);
        }

        public abstract void Move(string sourceFileName, string destFileName);

        public void Replace(string sourceFileName, string destinationFileName, string destinationBackupFileName)
        {
            Replace(sourceFileName, destinationFileName, destinationBackupFileName, false);
        }

        public abstract void Replace(string sourceFileName, string destinationFileName, string destinationBackupFileName,
                                     bool ignoreMetadataErrors);
    }
}